grammar plp4;

@header
{
	import java.lang.String;
}

@members
{
	final String TABULACION = "  ";
	final String TABINICIAL = "";
	TablaTipos tablaTipos = new TablaTipos();
	TablaSimbolos tablaSimbolos = new TablaSimbolos(tablaTipos);
	void mensajeError (String s)
	{
		System.err.println(s);
		System.exit (1);
	}
	
	String promociones (Integer inicio, Integer objetivo)
	{
		if (inicio == Tipo.BOOLEAN && objetivo == Tipo.DOUBLE)
		{
			return "conv.r8 \n";
		}
		if (inicio == Tipo.INT && objetivo == Tipo.DOUBLE)
		{
			return "conv.r8 \n";
		}
		if (inicio == Tipo.DOUBLE && objetivo == Tipo.INT)
		{
			return "conv.i4 \n";
		}
		return "";
	}
	int nE = 0;
	String nextLabel ()
	{
		return "L"+ (nE++);
	}

	final String header = ".assembly extern mscorlib {} \n .assembly 'Source' {} \n\n\n";
	
	final String classHeader = ".class 'single' extends [mscorlib]System.Object \n {/* Definicion del constructor */ \n .method public specialname rtspecialname instance void .ctor() cil managed \n { \n call instance void [mscorlib]System.Object::.ctor() \n ret \n }// fin .ctor \n";
	
	final String methodHeader = ".method static public void main () cil managed \n	{	\n .entrypoint	\n ";

	public void emitErrorMessage(String m)
	{
		System.err.println( m );
		System.exit (1);		
	}
	
	public String generarMetodo(String n, int num)
	{
		String ret = claseActual+ ".";
	
		if (n.charAt(n.length()-1) == '\'')
		{
			ret += n.substring(0, n.length()-1)+"(";
		}
		else
		{
			ret += n+"(";
		}
		
		if (num > 0)
		{
		ret += "double";
		}
		for (int i = 1; i < num; i++)
		{
			ret += ",double";
		}
		ret += ")";
		
		return ret;
	}
	
	public String llamadaMetodo(String n, int num)
	{
		String ret = n+"(";
		
		if (num > 0)
		{
		ret += "float64 ";
		}
		for (int i = 1; i < num; i++)
		{
			ret += ", float64";
		}
		ret += ")";
		
		return ret;
	}
	
	public boolean hayMain = false;
	public String claseActual;

}


@lexer::members
{
	public void emitErrorMessage(String m)
	{
		System.err.println( m );
		System.exit (1);		
	}
}

@rulecatch { 
  catch (RecognitionException re) { 
    reportError(re); 
    System.exit(1); 
  } 
} 

s : 
	clase [TABINICIAL + TABULACION]+
		{		
			if (!hayMain)
			{
				mensajeError("Error 18: debe existir un unico metodo Main");
			}
			System.out.println(header + tablaTipos.toString());
		};

clase [String tab]:
	CLASS 
	ID
	LLAVEI 
	{
		claseActual = $ID.text;
		if (tablaTipos.existe($ID.text))
		{
			mensajeError("CLASE DUPLICADA");
		}
		Tipo t = tablaTipos.addClase($ID.text);
		tablaSimbolos.push();
	}
	(
	miembro [$tab + TABULACION, t]
	)+
	LLAVED
	{
		tablaSimbolos.pop();
	};
	
metodo [String tab, Tipo clase]
@init{int tipo; Simbolo metodo; String trad;}: 
	PUBLIC 
	STATIC 
	VOID 
	MAIN 
	PARI 
	PARD 
	{
		clase.addMetodo($MAIN.text, -1, true, 0);
		tablaSimbolos.push();
	}
	bloque [$tab + TABULACION, false, "", "", "", Tipo.ERROR]
	{
		if (hayMain)
		{
			mensajeError("Error 18: debe existir un unico metodo Main");
		}
		hayMain = true;
		tablaSimbolos.popMetodo(clase);
		trad = ".maxstack " + ($bloque.maxStack+10) + "\n" + $bloque.trad + $tab + $tab + "ret \n";
		clase.getAtributos().buscar($MAIN.text, 0).setCuerpo(trad);
		tablaSimbolos.nuevoSimbolo($MAIN.text, Tipo.VOID, 0);
	}
	
	|
	
	
	PUBLIC
	{
		tipo = -1;
	}
	(tiposimple
	{
		tipo = $tiposimple.tipo;
	}
	)? 
	ID 
	{
		if ($ID.text.equals(clase.getNombre()))
		{
			if (tipo != -1)
			{
				mensajeError("Error 31 (" + $ID.getLine() + "," + ($ID.getCharPositionInLine()+1) +
				"): un constructor no puede devolver nada");
			}
		}
		else
		{
			if (tipo == -1)
			{
				mensajeError("Error 32 (" + $ID.getLine() + "," + ($ID.getCharPositionInLine()+1) +
				"): un metodo debe devolver algo");
			}
		}
		tablaSimbolos.push();
	}
	PARI 
	args 
	{
		if (clase.getAtributos().buscar($ID.text, $args.num) != null)
		{
			mensajeError("Error 20 ("+$ID.getLine()+","+($ID.getCharPositionInLine()+1)+
				"): el metodo '"+generarMetodo($ID.text, $args.num)+"' ya esta definido");
		}
		clase.addMetodo($ID.text, tipo, true,  $args.num);
		tablaSimbolos.nuevoMetodo($ID.text, tipo, $args.num);
	}
	PARD 
	bloque[$tab + TABULACION, false, "", "", "", tipo]
	{
		trad = ".maxstack " + $bloque.maxStack +"\n"+ $bloque.trad + $tab + "ret \n";
		tablaSimbolos.popMetodo(clase);
		clase.getAtributos().buscar($ID.text, $args.num).setCuerpo(trad);
		tablaSimbolos.clearParametros();
	};
	

declins [String tab, boolean breakPermitido, String etBucle, String etInc, String etFin, int tipo]
returns [String trad, Integer maxStack]
@init {$trad = "";$maxStack = 0;}:   
	( 
	instr[$tab + TABULACION, $breakPermitido, $etBucle, $etInc, $etFin, $tipo]
	{
		$trad += $instr.trad + '\n';
		$maxStack = Math.max($maxStack, $instr.maxStack);
	} 
	| 
	decl[true, null]
	{
		$trad += " // Declaracion de una variable \n";
		$maxStack = Math.max($maxStack, 0);
	}
	)*;

bloque [String tab, boolean breakPermitido, String etBucle, String etInc, String etFin, int tipo]
returns [String trad, Integer maxStack] : 
	LLAVEI 
	{
		tablaSimbolos.push();
	}
	declins [$tab + TABULACION, $breakPermitido, $etBucle, $etInc, $etFin, $tipo]
	{
		$trad = $declins.trad;
		$maxStack = $declins.maxStack;
		tablaSimbolos.pop();
	}
	LLAVED;

instr [String tab, boolean breakPermitido, String etBucle, String etInc, String etFin, int tipo]
returns [String trad, Integer maxStack]
@init {int simbAux; boolean simbNegativo;} :
	bloque	[$tab + TABULACION, $breakPermitido, $etBucle, $etInc, $etFin, $tipo]
	{
		$trad = $bloque.trad;
		$maxStack = $bloque.maxStack;
	}
	|
 	{
 		tablaSimbolos.push();
 	}
 	
 	IF PARI expr [$tab + TABULACION] PARD i1=instr[$tab + TABULACION, $breakPermitido, $etBucle, $etInc , $etFin, $tipo] 
 	{
 		$etBucle = nextLabel();
 		$etFin = nextLabel();
 		if ($expr.tipo != Tipo.BOOLEAN)
 		{
 			mensajeError ("Error 5 (" + $IF.getLine() + "," + ($IF.getCharPositionInLine()+1) +
					"): " + "la expresion debe ser de tipo booleano en la instruccion '" + $IF.text + "'");
 		}
 		$trad = $expr.trad;
 		$trad += $tab + "ldc.i4 0 \n " ;
 		$trad += $tab + "beq " + $etBucle + " //Si el valor de la condicion es false (0), vamos a else/fin \n";
 		$trad += $i1.trad;
 		$trad += $tab + "br " + $etFin + " //Despues de IF, salimos del condicional \n";
 		$trad += $etBucle + " : // Venimos aqui si no se ejecuta el IF\n";
 		$maxStack = Math.max($expr.maxStack, $i1.maxStack); 
 	}
 	( ELSE i2=instr[$tab + TABULACION,false,"","", "", $tipo] 
	{
		$trad += $tab + " //Instrucciones del else \n";
		$trad += $i2.trad;

		$maxStack = Math.max($maxStack, $i2.maxStack);		
	}
	)?
	{
		$trad += $etFin + " : //Venimos aqui despues del IF \n";
		tablaSimbolos.pop();
	}
	|
	WHILE PARI expr [$tab + TABULACION] PARD 
	{
		tablaSimbolos.push();
		if ($expr.tipo != Tipo.BOOLEAN)
		{
 			mensajeError ("Error 5 (" + $WHILE.getLine() + "," + ($WHILE.getCharPositionInLine()+1) +
					"): " + "la expresion debe ser de tipo booleano en la instruccion '" + $WHILE.text + "'");
		}
		$etBucle = nextLabel();
		$etFin = nextLabel();
	}
	i1=instr[$tab + TABULACION, true, $etBucle , $etInc, $etFin, $tipo]
	{
		$trad = $etBucle + " : \n";
		$trad += $expr.trad + " //Condicion del bucle \n";
		$trad += $tab + "ldc.i4 0 \n";
		$trad += $tab + "beq " + $etFin + "   //Si el valor de la condicion es false (0), vamos a fin \n";
		$trad += $i1.trad;
		$trad += "br " + $etBucle + " // Siguiente iteracion de while \n";
		$trad += $etFin + " : \n";
		
		$maxStack = Math.max($expr.maxStack+1, $i1.maxStack);
		
		tablaSimbolos.pop();
	}
	|
	FOREACH PARI VAR id1=ID IN subref[tab] PARD 
	{
		tablaSimbolos.push();
		$etBucle = nextLabel();
		$etFin = nextLabel();
		int valorVector;
		int iterador = tablaSimbolos.crearIndice("");
		Simbolo simb = $subref.lastSimbolo;

		if (simb == null)
		{
			mensajeError ("Error 2 (" + $subref.c + "," + ($subref.l) +
					"): simbolo '"+ $subref.lastSimbolo.getNombre() +"' no ha sido declarado");	
		}	
		if (!tablaTipos.isArray(simb.tipo))
		{
			mensajeError ("Error 11 (" + $subref.c + "," + ($subref.l) +
					"): " + "el identificador '" + $subref.lastSimbolo.getNombre() + "' no es de tipo array");
		}
		
		if (tablaTipos.getTipoBase(simb.tipo) == Tipo.DOUBLE)
		{
			valorVector = tablaSimbolos.crearIndice($id1.text, Tipo.DOUBLE);
		}
		else
		{
			valorVector =  tablaSimbolos.crearIndice($id1.text);
		}
	}
		i1=instr[$tab + TABULACION, true, $etBucle, $etInc, $etFin, $tipo]
	{
		$trad = $tab + "ldc.i4 -1 \n";
		$trad += $tab + "stloc " + iterador + " //Inicializacion de la variable que controla el array del foreach \n";
		
		$trad += $etBucle + " : //Etiqueta de inicio del foreach \n";
		$trad += $tab + "ldloc " + iterador + "\n";
		$trad += $tab + "ldc.i4 1 \n";
		$trad += $tab + "add \n";
		$trad += $tab + "dup \n";
		$trad += $tab + "stloc " + iterador + " //Almacenamos el iterador para posterior uso \n";
		$trad += $tab + "ldc.i4 " + tablaTipos.getTamanyo(simb.tipo);
		$trad += $tab + "beq " + $etFin + " //Salto a fin si fin de vector \n";
		

		$trad += $tab + "ldloc " + $subref.var + $tab + " //Array por el que iteramos \n";
		if ($subref.accesoClase != "")
		{
			$trad += $subref.accesoClase;
			$trad += tab + "ldloc" + $subref.ultimoAccesoClase;
		}
		
		$maxStack = 2 + $i1.maxStack;

		
		$trad += $tab + "ldloc " + iterador + " //Posicion del array (iterador)\n";
		if (tablaTipos.getTipoBase(simb.tipo) == Tipo.DOUBLE)
		{
			$trad += $tab + "ldelem.r8 \n";
		}
		else
		{
			$trad += $tab + "ldelem.i4 \n";
		}
		
		$trad += $tab + "stloc " + valorVector + " //Asignamos al valorVector \n";
		
		$trad += $i1.trad;
		$trad += $tab + "br " + $etBucle + "\n";
		$trad += $etFin + " : //Fin del foreach \n";
		
		$maxStack = Math.max($i1.maxStack, Math.max(3, $maxStack));
		
		tablaSimbolos.pop();
	}
	
	|
	
	FOR PARI INT ID
	{
		tablaSimbolos.push();

		simbAux = tablaSimbolos.crearIndice($ID.text);
		simbNegativo = false;
	}
	ASIG e1=expr[$tab + TABULACION] 
	{
		if ($e1.tipo != Tipo.INT && $e1.tipo != Tipo.DOUBLE)
		{
			mensajeError ("Error 6 (" + $ASIG.getLine() + "," + ($ASIG.getCharPositionInLine()+1) +
					"): " + "tipos incompatibles en la instruccion de asignacion");	
		}
	}
	 TO e2=expr[$tab + TABULACION] 
	 {
	 	if ($e2.tipo == Tipo.BOOLEAN)
		{
			mensajeError ("Error 17 (" + $TO.getLine() + "," + ($TO.getCharPositionInLine()+1) +
					"): " + "la expresion debe ser de tipo numerico");	
		}
	 }
	 STEP 
	(ADDOP
	{
		if ($ADDOP.text.equals("-"))
		{
			simbNegativo = true;
		}
	}
	)? ENTERO PARD 
	{
		$etBucle = nextLabel();
		$etFin = nextLabel();
	}
	i1=instr[$tab + TABULACION, true, $etBucle, $etInc, $etFin, $tipo]
	{
		String etAux = nextLabel();
		
		$trad = $e1.trad;
		if ($e1.tipo == Tipo.DOUBLE)
		{
			$trad += $tab + "conv.i4 //Convertimos inicializacion a entero porque era double \n";
		}
		$trad += "stloc " + simbAux + " //Cargamos la inicializacion en la variable \n";		
		$trad += "br " + etAux + " //Primera iteracion, saltamos el incremento \n";
		
		$trad += $etBucle + " : //Incremento, condicion \n";
		$trad += "ldloc " + simbAux + " \n";
		$trad += "ldc.i4 " + $ENTERO.text + " //Valor del incremento \n";
		if (simbNegativo)
		{
			$trad += "sub \n";
		}
		else
		{
			$trad += "add \n";
		} 
		$trad += "stloc " + simbAux + "\n";
		$trad += etAux + " : //Etiqueta auxiliar para la primera iteracion \n";
		
		$trad += "ldloc " + simbAux + " //Recuperamos el valor del iterador \n";
		$trad += promociones(tablaSimbolos.busca(simbAux).tipo, $e2.tipo) + " //promocion del iterador \n";
		$trad += $e2.trad;
		if (simbNegativo)
		{
			$trad += "blt " + $etFin + " //Decrementamos el for, si es menor salimos \n";
		}
		else
		{
			$trad += "bgt " + $etFin + " //Incrementamos el for, si es mayor salimos \n";
		}
		
		$trad += $i1.trad;
		
		$trad += "br " + etBucle + " //Fin de bucle, siguiente iteracion \n";
	
	 	$trad += $etFin + " : //Fin de bucle\n";
	 	
	 	$maxStack = Math.max($e1.maxStack+1, Math.max($e2.maxStack+1, $i1.maxStack));
		tablaSimbolos.pop();
	}
	|	
	BREAK PYC
	{
		if (breakPermitido)
		{
			$trad = "br " + $etFin + " // Salida del bucle por BREAK \n";	
		}
	 	else
	 	{
	 		mensajeError ("Error 16 (" + $BREAK.getLine() + "," + ($BREAK.getCharPositionInLine()+1) +
				"): instruccion '" + $BREAK.text + "' no permitida fuera de un bucle");	
	 		//Error 16 (fila,columna): instruccion 'lexema' no permitida fuera de un bucle
	 	}
	 	$maxStack = 0;
	}
	|
	CONTINUE PYC
	{
		if (breakPermitido)
		{
			$trad = "br " + $etBucle + " // Iteracion por CONTINUE \n";	
		}
	 	else
	 	{
	 		mensajeError ("Error 16 (" + $CONTINUE.getLine() + "," + ($CONTINUE.getCharPositionInLine()+1) +
				"): instruccion '" + $CONTINUE.text + "' no permitida fuera de un bucle");
	 	}
	 	$maxStack = 0;
	}
	|	
	ref[$tab + TABULACION]
	{
		String aux1 = $ref.aClase;
		String aux2 = $ref.uaClase;
	}
	
	 cambio [$tab + TABULACION, $ref.tipo, $ref.var, $ref.accesoArray,$ref.aClase, $ref.uaClase, $ref.maxStack, $ref.isParam]
	{
		$trad = $cambio.trad;
		$maxStack = Math.max($cambio.maxStack+1, $ref.maxStack);
	}
	|	
	ID
	{
		Simbolo vect = tablaSimbolos.busca($ID.text);
		
		if (vect == null)
		{
			mensajeError ("Error 2 (" + $ID.getLine() + "," + ($ID.getCharPositionInLine()+1) +
					"): " + "simbolo '" + $ID.text + "' no ha sido declarado");		

		}
		if (vect.tipoSimbolo == Simbolo.INDICE)
		{
			mensajeError("Modificando indice");
		}
		if (!tablaTipos.isArray(vect.tipo))
		{
			mensajeError ("Error 11 (" + $ID.getLine() + "," + ($ID.getCharPositionInLine()+1) +
					"): " + "el identificador '" + $ID.text + "' no es de tipo array");
		}
		vect.inicializado = true;
	}
	ASIG NEW tiposimple CORI dims[$ID.text] CORD PYC 
	{
		int tipo2 = tablaTipos.desRef($dims.tipo);

		
		if (tablaTipos.isArray(tipo2))
		{
			mensajeError ("Error 10 (" + $CORD.getLine() + "," + ($CORD.getCharPositionInLine()+1) +
					"): " + "numero de dimensiones incorrecto");
		}
	
		$trad = "ldc.i4 " + $dims.tam + "\n //Reserva de memoria para el array " + $instr.text + " \n";
		$trad += "newarr ";
		
		if ($tiposimple.tipo != tipo2)
		{
			mensajeError ("Error 14 (" + $tiposimple.cc + "," + ($tiposimple.cl) +
					"): " + "tipo '" + $tiposimple.text + "' incompatible con la declaracion");
 
		}
		
		if (tipo2 == Tipo.INT || tipo2 == Tipo.BOOLEAN)
		{
			$trad += "[mscorlib]System.Int32 \n";
		}
		else
		{
			$trad += "[mscorlib]System.Double \n";
		}
		$trad += "stloc " + tablaSimbolos.getPos($ID.text) + " \n";
		$maxStack = 1;
	}
	| 
	WRITELINE PARI expr[$tab + TABULACION] PARD PYC
	{
		$trad = $expr.trad;
		$trad += $tab;
		
		if ($expr.tipo == Tipo.BOOLEAN)
		{
			$trad += "call void [mscorlib]System.Console::WriteLine(bool) \n";
		}
		else if ($expr.tipo == Tipo.INT)
		{
			$trad += "call void [mscorlib]System.Console::WriteLine(int32) \n";
		}
		else if ($expr.tipo == Tipo.DOUBLE)
		{
			$trad += "call void [mscorlib]System.Console::WriteLine(float64) \n";
		}
		else
		{
			mensajeError ("Error 28 (" + $WRITELINE.getLine() + "," + ($WRITELINE.getCharPositionInLine()+1) +
				"): la referencia es de tipo objeto");
		}
		$maxStack = $expr.maxStack;
	}
	
	
	| 
	
	
	RETURN 
	{
		if ($tipo == Tipo.ERROR)
		{
			mensajeError( "Error 22 (" + $RETURN.getLine() + "," + ($RETURN.getCharPositionInLine()+1) +
				"): aqui no puede usarse return");
		}
	}
	expr[tab]
	{
		$trad = $expr.trad +"  //valor de retorno\n";
		
		int a = $tipo;
		int b = $expr.tipo;
		int newTipo = OperaTipos.opAsig(a, b);
		if (newTipo == Tipo.ERROR)
		{
			mensajeError ("Error 23 (" + $RETURN.getLine() + "," + ($RETURN.getCharPositionInLine()+1) +
					"): " + "valor devuelto de tipo incompatible");
		}
		
		$trad += $tab + promociones($expr.tipo, newTipo) + " //Promocion de retorno \n";
		$trad += "ret \n";
		$maxStack = $expr.maxStack;
	
	}PYC 
	
	| 
	
	
	id1=ID 
	{
		Simbolo izq;
		izq = tablaSimbolos.busca($id1.text);
		
		if (izq == null)
		{
			mensajeError ("Error 2 (" + $id1.getLine() + "," + ($id1.getCharPositionInLine()+1) +
					"): simbolo '"+ $id1.text +"' no ha sido declarado");	
		}

	}
	ASIG NEW id2=ID
	{
		int der;
		
		der = tablaTipos.getTipo($id2.text);
		
		if (der == Tipo.ERROR)
		{
			mensajeError ("Error 19 (" + $id1.getLine() + "," + ($id1.getCharPositionInLine()+1) +
					"): identificador '"+ $id1.text +"' usado incorrectamente");	
		}
		
		
		if (der != izq.getTipo())
		{
			mensajeError("Error 26 (" + $id1.getLine() + "," + ($id1.getCharPositionInLine()+1) +
				"): objeto '"+ $id1.text +"' no es de clase '" + $id2.text +"'");
		}
	}
	 PARI params[tab, $PARI.getLine(), ($PARI.getCharPositionInLine()+1)] PARD PYC 
	
	{
		$trad = $params.trad;
		$trad += "newobj instance void '" + $id2.text + "'::.ctor(" + $params.parametrosMetodo + ") \n";
		$trad += "stloc " + tablaSimbolos.getPos($id1.text);  
		$maxStack = Math.max(1, $params.maxStack);
	}
	
	| 
	
	subref [tab]
	PARI 
	params [tab , $PARI.getLine(), ($PARI.getCharPositionInLine()+1)]
	PARD 
	{
		Simbolo simb;
		if ($subref.ts == null)
		{
			simb = tablaSimbolos.buscar($subref.lastSimbolo.getNombre(), $params.numParam);
		}
		else
		{
			simb = $subref.ts.buscar($subref.lastSimbolo.getNombre(), $params.numParam);
		}
		
		
		if (simb == null)
		{
			mensajeError ("Error 21 (" + $subref.c + "," + ($subref.l) +
				"): el metodo '"+ generarMetodo($subref.ultimoAccesoClase, $params.numParam) +"' no esta definido");
		}
		
		$trad = $params.trad;
		$trad += $subref.accesoClase;
		$trad += $trad + "call instance ";
		
		if ($subref.tipo == Tipo.INT || $subref.tipo == Tipo.BOOLEAN)
		{
			$trad += "int32 ";
		}
		else if ($subref.tipo == Tipo.DOUBLE)
		{
			$trad += "float64 ";
		}
		else
		{
			$trad += "ERROR ";
		}
		
		$trad += $subref.ultimoAccesoClase + "(" + $params.parametrosMetodo + ") // LLAMADA A UN METODO LIBRE\n";
		
		$trad += "pop //Sacamos lo q devuelve el metodo para q no estorbe \n";
		$maxStack = $params.maxStack;

	}
	PYC;
	
	
	


dims [String var]
returns [Integer tam, Integer tipo] 
@init {$tam = 1;}: 
	e1=ENTERO 
	{
		$tipo = tablaSimbolos.busca(var).tipo;
		
		if (Integer.parseInt($e1.text)<1)
		{
			mensajeError ("Error 8 (" + $e1.getLine() + "," + ($e1.getCharPositionInLine()+1) +
					"): " + "tamanyo incorrecto");
		}
		
		tablaTipos.get($tipo).setTamanyo(Integer.parseInt($e1.text));
		$tam*=Integer.parseInt($e1.text);
	}
	(COMA e2=ENTERO
	{
		$tipo = tablaTipos.desRef($tipo);
		
		if (Integer.parseInt($e2.text) < 1)
		{
			mensajeError ("Error 8 (" + $e2.getLine() + "," + ($e2.getCharPositionInLine()+1) +
					"): " + "tamanyo incorrecto");
		}
		
		if (!tablaTipos.isArray($tipo))
		{
			mensajeError ("Error 10 (" + $COMA.getLine() + "," + ($COMA.getCharPositionInLine()+1) +
					"): " + "numero de dimensiones incorrecto");
		}
		
		tablaTipos.get($tipo).setTamanyo(Integer.parseInt($e2.text));
		$tam*=Integer.parseInt($e2.text);
	}
	)*
	;

cambio  [String tab, Integer tipo, Integer var, String accesoArray, String accesoClase, String ultimoAccesoClase, int maxStackRef, boolean isParam]
returns [String trad, Integer maxStack] : 
	ASIG expr[$tab+TABULACION] PYC
	{
		$trad = "";
		
		if (tablaTipos.get($tipo).isClass())
		{
			mensajeError("Error 19 (" + $ASIG.getLine() + "," + ($ASIG.getCharPositionInLine()+1)+
				"): identificador '"+ $ultimoAccesoClase +"' usado incorrectamente");
		}
		
		int newTipo = OperaTipos.opAsig(tipo, $expr.tipo);
		
		if (newTipo == Tipo.ERROR)
		{
			mensajeError ("Error 6 (" + $ASIG.getLine() + "," + ($ASIG.getCharPositionInLine()+1) +
					"): " + "tipos incompatibles en la instruccion de asignacion");
		}

		if (tablaSimbolos.busca(var) != null)
		{
			if (tablaSimbolos.busca(var).tipoSimbolo == Simbolo.INDICE)
			mensajeError ("Error 15 (" + $ASIG.getLine() + "," + ($ASIG.getCharPositionInLine()+1) +
					"): la variable que se intenta modificar es una variable indice");
		}
	
		if (accesoArray.equals(""))
		{
			$trad += $expr.trad + $tab + " //Valor de asignacion \n";
			$trad += $tab + promociones($expr.tipo, newTipo) + " //Promocion de asig \n";
			if (!$ultimoAccesoClase.equals(""))
			{
				$trad = $tab + "ldloc " + $var + $tab + " //Clase base en el que vamos a asignar \n";
				$trad += $accesoClase;
				$trad += $expr.trad + $tab + " //Valor de asignacion \n";
				$trad += $tab + promociones($expr.tipo, newTipo) + " //Promocion de asig \n";
				$trad += tab + "stfld ";
				
				if (tipo == Tipo.INT || tipo == Tipo.BOOLEAN)
				{
					$trad += "int32 ";
				}
				else
				{
					$trad += "float64 ";
				}
				
				$trad += $ultimoAccesoClase;

				$maxStack = $maxStackRef+1;
			}
			else
			{
				$maxStack = 0;
				if (isParam)
					$trad += $tab + "starg " + $var + "\n";
				else
					$trad += $tab + "stloc " + $var + "\n";
			}
			$maxStack += $expr.maxStack;
		}
		else
		{
			$trad += $tab + "ldloc " + $var + $tab + " //Array en el que vamos a asignar \n";
			if (!$accesoClase.equals(""))
			{
				$trad += $accesoClase;
				$trad += tab + "stloc " + $ultimoAccesoClase;
			}
			$trad += $accesoArray + $tab + " //Posicion del array \n";
			$trad += $expr.trad + $tab + " //Valor que asignamos al array \n";
			$trad += $tab + promociones($expr.tipo, newTipo) + " //Promocion de asig \n";
			if (tipo == Tipo.INT || tipo == Tipo.BOOLEAN)
			{
				$trad += $tab + "stelem.i4 //Cargamos el entero en el array \n";
			}
			else
			{
				$trad += $tab + "stelem.r8 //Cargamos el double en el array desde 683 \n";
			}
			$maxStack = 2 + $expr.maxStack;
		}
	} 
	|
	PUNTO READLINE PYC
	{
		$trad = "";
		if (tablaTipos.get($tipo).isClass())
		{
			mensajeError ("Error 28 (" + $READLINE.getLine() + "," + ($READLINE.getCharPositionInLine()+1) +
					"): la referencia es de tipo objeto");
		}
	
		if (tablaSimbolos.busca(var).tipoSimbolo == Simbolo.INDICE)
		{
			mensajeError ("Error 15 (" + $READLINE.getLine() + "," + ($READLINE.getCharPositionInLine()+1) +
					"): la variable que se intenta modificar es una variable indice");
		}
		
		if (	(tipo == Tipo.INT && !$READLINE.text.equals("int.Parse(System.Console.ReadLine())")) ||
			(tipo == Tipo.DOUBLE && !$READLINE.text.equals("double.Parse(System.Console.ReadLine())")) ||
			(tipo == Tipo.BOOLEAN && !$READLINE.text.equals("bool.Parse(System.Console.ReadLine())")))
		{
			mensajeError ("Error 7 (" + $READLINE.getLine() + "," + ($READLINE.getCharPositionInLine()+1) +
					"): " + "tipos incompatibles en la instruccion de lectura");
		}

	
		if (accesoArray.equals(""))
		{
			if (!$accesoClase.equals(""))
			{
				$trad += $tab + "ldloc " + $var + $tab + " //Clase base en el que vamos a asignar \n";
				$trad += $accesoClase;
				if ($tipo == Tipo.DOUBLE)
				{
					$trad += $tab + "call string [mscorlib]System.Console::ReadLine() \n " + 
					$tab + "call float64 [mscorlib]System.Double::Parse(string)\n";
				}
				else if ($tipo == Tipo.INT)
				{
					$trad += $tab + "call string [mscorlib]System.Console::ReadLine() \n " + 
						$tab + "call int32 [mscorlib]System.Int32::Parse(string) \n";
				}
				else
				{
					$trad += $tab + "call string [mscorlib]System.Console::ReadLine() \n " + 
						$tab + "call bool [mscorlib]System.Boolean::Parse(string) \n ";
				}
				$trad += tab +  $ultimoAccesoClase;
			}
			else
			{
				if ($tipo == Tipo.DOUBLE)
				{
					$trad = $tab + "call string [mscorlib]System.Console::ReadLine() \n " + 
					$tab + "call float64 [mscorlib]System.Double::Parse(string)\n";
				}
				else if ($tipo == Tipo.INT)
				{
					$trad = $tab + "call string [mscorlib]System.Console::ReadLine() \n " + 
						$tab + "call int32 [mscorlib]System.Int32::Parse(string) \n";
				}
				else
				{
					$trad = $tab + "call string [mscorlib]System.Console::ReadLine() \n " + 
						$tab + "call bool [mscorlib]System.Boolean::Parse(string) \n ";
				}
				$trad += $tab + "stloc " + $var + "\n";
			}
			$maxStack = 1;
		}
		else
		{
			$trad += $tab + "ldloc " + $var + " //Array en el que vamos a asignar \n";
	
			if ($accesoClase != "")
			{
				$trad += $accesoClase;
				$trad += tab + "stloc " + $ultimoAccesoClase;
			}
			
			$trad += $tab + $accesoArray + " //Posicion del array \n";
			if ($tipo == Tipo.DOUBLE)
			{
				$trad += $tab + "call string [mscorlib]System.Console::ReadLine() \n " + 
				$tab + "call float64 [mscorlib]System.Double::Parse(string)\n";
			}
			else if ($tipo == Tipo.INT)
			{
				$trad += $tab + "call string [mscorlib]System.Console::ReadLine() \n " + 
					$tab + "call int32 [mscorlib]System.Int32::Parse(string) \n";
			}
			else
			{
				$trad += $tab + "call string [mscorlib]System.Console::ReadLine() \n " + 
					$tab + "call bool [mscorlib]System.Boolean::Parse(string) \n ";
			}
			if (tipo != Tipo.DOUBLE)
			{
				$trad += $tab + "stelem.i4 //Cargamos el entero en el array \n";
			}
			else
			{
				$trad += $tab + "stelem.r8 //Cargamos el double en el array \n";
			}
			$maxStack = Math.max($maxStackRef+2, 3);
		}
	};


tiposimple
returns [Integer tipo, Integer cc, Integer cl] : 
	INT {$tipo = Tipo.INT; $cc = $INT.getLine(); $cl =($INT.getCharPositionInLine()+1);}
	|
	DOUBLE {$tipo = Tipo.DOUBLE; $cc = $DOUBLE.getLine(); $cl =($DOUBLE.getCharPositionInLine()+1);}
	| 
	BOOL { $tipo = Tipo.BOOLEAN; $cc = $BOOL.getLine(); $cl =($BOOL.getCharPositionInLine()+1);};

decl[boolean isPublic, Tipo clase]
: 
 	tipo varid[$tipo.tipo, $clase, $isPublic] ( COMA varid[$tipo.tipo, $clase, $isPublic] )* PYC;

varid [Integer tipo, Tipo clase, boolean visibilidad] 
@init {Integer aridad = 0; Integer tipoAux = 0;}: 
	ID 
	{
		if ($clase != null)
		{
			if ($clase.getAtributos().existe($ID.text))
			{
				mensajeError ("Error 1 (" + $ID.getLine() + "," + ($ID.getCharPositionInLine()+1) +
					"): '" + $ID.text + "' ya existe en este ambito");
			}
		}
		else if (tablaSimbolos.existe ($ID.text))
		{
			mensajeError ("Error 1 (" + $ID.getLine() + "," + ($ID.getCharPositionInLine()+1) +
					"): '" + $ID.text + "' ya existe en este ambito");
		}
	}
	(CORI {aridad++;}
	(COMA {aridad++;})* 
	CORD )?
	{
		if (aridad > 0 && tablaTipos.get(tipo).isClass())
		{
			mensajeError("Error 25 (" + $CORI.getLine() + "," + ($CORI.getCharPositionInLine()+1) +
				"): no se permite la declaracion de arrays de objetos");
		}
		tipoAux = tablaTipos.add(tipo, aridad);
		if ($clase != null)
		{
			$clase.addCampo($ID.text, tipoAux, $visibilidad, tablaTipos);
			tablaSimbolos.nuevoSimbolo ($ID.text, Simbolo.INTERNO ,tipoAux);
		}
		else
		{
			tablaSimbolos.nuevoSimbolo ($ID.text, Simbolo.VAR ,tipoAux);
		}
	};

expr [String tab]
returns [Integer tipo, String trad, Integer maxStack]: 
	e1=eand [$tab + TABULACION]
	{
		$tipo = $e1.tipo;
		$trad = $e1.trad;	
		
		$maxStack = $e1.maxStack;
	}
	( OR
	{
		$tipo = OperaTipos.opLog(Tipo.BOOLEAN, $tipo);
		
		if ($tipo == Tipo.ERROR)
		{
			mensajeError ("Error 4 (" + $OR.getLine() + "," + ($OR.getCharPositionInLine()+1) +
					"): " + "tipo incompatible en operador logico '"+ $OR.text + "'");
		}
	}
	e2=eand [$tab + TABULACION]
	{
		$tipo = OperaTipos.opLog($e2.tipo, $tipo);
		
		if ($tipo == Tipo.ERROR)
		{
			mensajeError ("Error 4 (" + $OR.getLine() + "," + ($OR.getCharPositionInLine()+1) +
					"): " + "tipo incompatible en operador logico '"+ $OR.text + "'");
		}
		
		$trad += $e2.trad;
		$trad += $tab + "or \n";
		
		$maxStack = Math.max($maxStack, $e2.maxStack +1);
	} 
	)*;

eand	[String tab]
returns [Integer tipo, String trad, Integer maxStack] : 
	e1=erel [$tab + TABULACION]	
	{
		$tipo = $e1.tipo;
		$trad = $e1.trad;
		
		$maxStack = $e1.maxStack;
	}
	( AND 
	{
		$tipo = OperaTipos.opLog(Tipo.BOOLEAN, $tipo);
		if ($tipo == Tipo.ERROR)
		{
			mensajeError ("Error 4 (" + $AND.getLine() + "," + ($AND.getCharPositionInLine()+1) +
					"): " +"tipo incompatible en operador logico '" + $AND.text + "'");
		}
	}
	e2=erel [$tab + TABULACION]
	{
		$tipo = OperaTipos.opLog($e2.tipo, $tipo);
		if ($tipo == Tipo.ERROR)
		{
			mensajeError ("Error 4 (" + $AND.getLine() + "," + ($AND.getCharPositionInLine()+1) +
					"): " +"tipo incompatible en operador logico '" + $AND.text + "'");
		}
		
		$trad += $e2.trad;
		$trad += $tab + "and \n";
		
		$maxStack = Math.max($maxStack, $e2.maxStack+1);
	}
	)*;

erel [String tab]
returns [Integer tipo, String trad, Integer maxStack]: 
	e1=esum	[$tab + TABULACION]
	{
		$tipo = $e1.tipo;
		$trad = $e1.trad;
		
		$maxStack = $e1.maxStack;
	}
	( RELOP 
	e2=esum [$tab + TABULACION]
	{
		String aux1="", aux2="";
		if ($e2.tipo != $tipo)
		{
			if ($e2.tipo == Tipo.DOUBLE || $tipo == Tipo.DOUBLE)
			{
				if ($e2.tipo != Tipo.DOUBLE)
				{
					aux2 = "conv.r8 //Promocion de relacional \n ";
				}
				else
				{
					aux1 = "conv.r8 //Promocion de relacional \n";
				}
			}
		}
		$tipo = OperaTipos.opRel($e2.tipo, $tipo);
		
		if ($tipo == Tipo.ERROR)
		{
			mensajeError ("Error 29 (" + $RELOP.getLine() + "," + ($RELOP.getCharPositionInLine()+1) +
				"): " +"tipo incompatible en operador relacional '"+ $RELOP.text + "'");	
			//No deberia salir, todo es valido
		}
		
		$trad += aux1;	//$tab + promociones ($e1.tipo, $tipo) + " //Promocion de e1 \n";
		
		$trad += $e2.trad;
		
		$trad += aux2; //$tab + promociones ($e2.tipo, $tipo) + " //Promocion de e2 \n";
		
		if ($RELOP.text.equals("=="))
		{
			$trad += $tab + "ceq \n";
		}
		else if ($RELOP.text.equals("!="))
		{
			$trad += $tab + "ceq \n" +
				 $tab + "ldc.i4 0 \n" +
				 $tab + "ceq \n";		
		}
		else if ($RELOP.text.equals("<"))
		{
			$trad += $tab + "clt \n";
		}
		else if ($RELOP.text.equals(">"))
		{
			$trad += $tab + "cgt \n";
		}
		else if ($RELOP.text.equals("<="))
		{
			$trad += $tab + "cgt \n" +
				 $tab + "ldc.i4 0 \n" +
				 $tab + "ceq \n";			
		}
		else if ($RELOP.text.equals(">="))
		{
			$trad += $tab + "clt \n" +
				 $tab + "ldc.i4 0 \n" +
				 $tab + "ceq \n";
		}
		
		$maxStack = Math.max($maxStack, $e2.maxStack+1);
	}
	)*;

esum [String tab]
returns [Integer tipo, String trad, Integer maxStack]: 
	t1=term [$tab + TABULACION]
	{
		$tipo = $t1.tipo;
		$trad = $t1.trad;
		$maxStack = $t1.maxStack;
	}
	( ADDOP 
	{
		$tipo = OperaTipos.opAri($t1.tipo, Tipo.INT);
		if ($tipo == Tipo.ERROR)
		{
			mensajeError ("Error 3 (" + $ADDOP.getLine() + "," + ($ADDOP.getCharPositionInLine()+1) +
				"): " +"tipo incompatible en operador aritmetico '"+ $ADDOP.text + "'");		
		}
	}
	t2=term [$tab + TABULACION]
	{
		$tipo = OperaTipos.opAri($t2.tipo, $tipo);
		if ($tipo == Tipo.ERROR)
		{
			mensajeError ("Error 3 (" + $ADDOP.getLine() + "," + ($ADDOP.getCharPositionInLine()+1) +
					"): " +"tipo incompatible en operador aritmetico '"+ $ADDOP.text + "'");
		}
		$trad += $tab + promociones ($t1.tipo, $tipo) + " //Pomocion de t1 \n";
		
		$trad += $t2.trad;
		
		$trad += $tab + promociones ($t2.tipo, $tipo) + " //Promocion de t2 \n";
	
		if ($ADDOP.text.equals("+"))
		{
			$trad += $tab + "add \n";
		}
		else
		{
			$trad += $tab + "sub \n";
		}
		$maxStack = Math.max($maxStack, $t2.maxStack+1);
	} 
	)*;

term [String tab]
returns [Integer tipo, String trad, Integer maxStack]: 
	f1=factor[$tab + "   "]
	{
		$tipo = $f1.tipo;
		$trad = $f1.trad;
		$maxStack = $f1.maxStack;
	}
	( MULOP 
	{
		$tipo = OperaTipos.opAri(Tipo.INT, $tipo);
						
		if ($tipo == Tipo.ERROR)
		{
			mensajeError ("Error 3 (" + $MULOP.getLine() + "," + ($MULOP.getCharPositionInLine()+1) +
					"): " + "tipo incompatible en operador aritmetico '" + $MULOP.text + "'");		
		}	
	}
	f2=factor [$tab + TABULACION]
	{
		$tipo = OperaTipos.opAri($f2.tipo, $tipo);
						
		if ($tipo == Tipo.ERROR)
		{
			mensajeError ("Error 3 (" + $MULOP.getLine() + "," + ($MULOP.getCharPositionInLine()+1) +
					"): " + "tipo incompatible en operador aritmetico '" + $MULOP.text + "'");		
		}
		
		$trad += $tab + promociones ($f1.tipo, $tipo) + " //Promocion de f1 \n";
		
		$trad += $f2.trad;
		
		$trad += $tab + promociones ($f2.tipo, $tipo) + " //Promocion de f2 \n";
		
		if ($MULOP.text.equals("*"))
		{
			$trad += $tab + "mul \n";
		}
		else
		{
			$trad += $tab + "div \n";
		}
		$maxStack = Math.max($maxStack, $f2.maxStack+1);
	}
	)*;

factor [String tab]
returns [Integer tipo, String trad, Integer maxStack]: 
	base [$tab + TABULACION]
	{
		$tipo = $base.tipo;
		$trad = $base.trad;	
		$maxStack = $base.maxStack;
	}
	| 
	NOT f1=factor [$tab + TABULACION]
	{
		if ($f1.tipo != Tipo.BOOLEAN)
		{
			mensajeError ("Error 4 (" + $NOT.getLine() + "," + ($NOT.getCharPositionInLine()+1) +
					"): " + "tipo incompatible en operador logico '" + $NOT.text + "'");
		}
		$tipo = $f1.tipo;
		$trad = $f1.trad + '\n' +
			$tab + "ldc.i4 0 \n" +
			$tab + "ceq //Comparamos con 0 para negar el valor \n";
		$maxStack = $f1.maxStack + 1;
	}
	| 
	PARI ADDOP f1=factor[$tab+TABULACION] PARD 
	{
		$trad = $f1.trad + '\n';
		if ($ADDOP.text.equals("-"))
		{
			$trad += $tab + "neg //Parentesis negado \n";
		}
		$tipo = $f1.tipo;
		$maxStack = $f1.maxStack;
			
	};

base [String tab]
returns [Integer tipo, String trad, Integer maxStack]: 
	ENTERO 
	{
		$tipo = Tipo.INT;
		$trad = $tab + "ldc.i4 " + $ENTERO.text + '\n';	
		$maxStack = 1;
	} 
	| 
	REAL  
	{
		$tipo = Tipo.DOUBLE;
		$trad = $tab + "ldc.r8 " + $REAL.text + '\n';
		$maxStack = 1;
	}
	| 
	BOOLEANO 
	{
		$tipo = Tipo.BOOLEAN;
		if ($BOOLEANO.text.equals("True"))
		{
			$trad = $tab + "ldc.i4 1 //Un True \n";
		}
		else
		{
			$trad = $tab + "ldc.i4 0 //Un False \n";
		}
		$maxStack = 1;
	}
	| 
	PARI expr [$tab+TABULACION] PARD 
	{
		$tipo = $expr.tipo;
		$trad = $tab + " // --- ( --- \n";
		$trad += $expr.trad;
		$trad += $tab + " // --- ) --- \n";	
		$maxStack = $expr.maxStack;
	}
	| 
	ref [$tab+TABULACION] 
	{
		$tipo = $ref.tipo;


		{
			
			if ($ref.isParam)
			{
				$trad += "ldarg " + $ref.var + " \n";
			}
			else
			{
				$trad = $tab + "ldloc " + $ref.var + " \n";
				if (!$ref.uaClase.equals(""))
				{
					$trad += $ref.aClase;
					$trad += "ldfld ";
					
				if ($ref.lastSimbolo.getTipo() == Tipo.INT || $ref.lastSimbolo.getTipo() == Tipo.BOOLEAN)
				{
					$trad += "int32 ";
				}
				else
				{
					$trad += "float64 ";
				}
				$trad += $ref.uaClase;
				}		
				
				if (!$ref.accesoArray.equals(""))
				{
					$trad += $ref.accesoArray;
					if ($ref.tipo == Tipo.DOUBLE)
					{
						$trad += $tab + "ldelem.r8 //Acceso a un array de doubles \n";
					}
					else
					{
						$trad += $tab + "ldelem.i4 //Acceso a un array de enteros o booleanos \n";
					}
				}
			}
		}
		$maxStack = $ref.maxStack;
	}
	
	
	|
	
	subref	[tab]
	{
		$tipo = $subref.tipo;
		$maxStack = 0;
		$trad = $tab + "ldloc " + $subref.var + " \n";
		
		if ($subref.accesoClase != "")
		{
			$trad += $subref.accesoClase;
			$trad += "ldfld" + $subref.ultimoAccesoClase;
		}		
	
	
	}
	PARI 
	params[tab, $PARI.getLine(),  ($PARI.getCharPositionInLine()+1)]
	{
		$maxStack = $params.maxStack + 1;
		Simbolo s;
		if ($subref.ts == null)
		{
			s = tablaSimbolos.buscar($subref.lastSimbolo.getNombre(), $params.numParam);
		}
		else
		{
			s = $subref.ts.buscar($subref.lastSimbolo.getNombre(), $params.numParam);
		}
		
		if (s == null)
		{
			mensajeError ("Error 21 (" + $subref.c + "," + ($subref.l) +
					"): el metodo '"+ generarMetodo($subref.ultimoAccesoClase, $params.numParam) +"' no esta definido");
		}
	
		$trad += $params.trad;
		
		$trad += "call instance ";
		
		if ($subref.tipo == Tipo.INT || $subref.tipo == Tipo.BOOLEAN)
		{
			$trad += "int32 ";
		}
		else
		{
		
		}	$trad += "float64 ";
		
		$trad += $subref.ultimoAccesoClase + llamadaMetodo($subref.lastSimbolo.getNombre(), $params.numParam) + " //LLAMADA A METODO \n";
	}
	PARD;

ref 	[String tab]
returns [Integer tipo, Integer var, String accesoArray, String aClase, String uaClase, Integer maxStack, boolean isParam, Simbolo lastSimbolo]
@init {Simbolo simbAux;}: 
	subref [tab]
	{	
		String aux1 = $subref.accesoClase ;
		String aux2 = $subref.ultimoAccesoClase;
		
		$tipo = $subref.tipo;
		$aClase = aux1;
		$uaClase = aux2;
		
		$var = $subref.var;
		$accesoArray = "";
		$maxStack = $subref.maxStack;
		simbAux = $subref.lastSimbolo;
		$lastSimbolo = simbAux;
		$isParam = $subref.isParam;
	}
	(CORI 
	{
		if (!tablaTipos.isArray(simbAux.tipo))
		{
			mensajeError("Error 11 (" + $subref.c + "," + ($subref.l) +
					"): el identificador '"+ $subref.lastSimbolo.getNombre() +"' no es de tipo array");
		}
	}
	
	indices[$tab+TABULACION, simbAux.tipo, $subref.accesoClase, $CORI.getLine(), $CORI.getCharPositionInLine()+1] 
	{
		$tipo = $indices.tipoFinal;
		$accesoArray = $indices.trad + " //Acceso a un array" + '\n';
		$maxStack = $subref.maxStack+$indices.maxStack;
	}
	CORD)?
	{
		if (tablaTipos.isArray($tipo))
		{
			mensajeError ("Error 9 (" + $subref.c + "," + ($subref.l) +
					"): " + "numero insuficiente de indices en el array '" + $subref.lastSimbolo.getNombre() + "'");
		}
	};

indices [String tab, Integer tipoInicial, String id, int cl, int cc]
returns [Integer tipoFinal, String trad, Integer maxStack] : 
	ex1=expr [$tab + TABULACION]
	{
		if ($ex1.tipo != Tipo.INT && $ex1.tipo != Tipo.DOUBLE)
		{
		 	mensajeError ("Error 13 (" + cl + "," + cc +
					"): " + "indice de tipo incompatible");
		}
		
		$tipoFinal = tablaTipos.desRef($tipoInicial);
		if ($tipoFinal == null)
		{
			mensajeError ("Error 12 (" + cl + "," + cc +
					"): demasiados indices");
		}
		
		$trad = $tab + $ex1.trad + " //Calculo del primer indice \n";
		
		
		
		if ($ex1.tipo == Tipo.DOUBLE)
		{
			$trad += "conv.i4 //Convertimos double a entero \n";
		}
		$maxStack = $ex1.maxStack;
	} 
	(COMA 
	ex2=expr [$tab + TABULACION]
	{
	
		if (!tablaTipos.isArray($tipoFinal))
		{
			mensajeError ("Error 12 (" + $COMA.getLine() + "," + ($COMA.getCharPositionInLine()+1) +
					"): demasiados indices");
		}
	
		$trad += $tab + "ldc.i4 " + tablaTipos.get($tipoFinal).getTamanyo().toString() + "\n";
		$trad += $tab + "mul \n";
		
		if ($ex2.tipo != Tipo.INT && $ex2.tipo != Tipo.DOUBLE)
		{
			mensajeError ("Error 13 (" + $COMA.getLine() + "," + ($COMA.getCharPositionInLine()+1) +
					"): indice de tipo incompatible");	
		}
		
		$trad += $tab + $ex2.trad + " //Calculo del siguiente indice \n";
		
		
		if ($ex2.tipo == Tipo.DOUBLE)
		{
			$trad += $tab + "conv.i4 //Convertimos double a entero \n";
		}
		
		$trad += $tab + "add \n";

		
		$tipoFinal = tablaTipos.desRef($tipoFinal);
		if ($tipoFinal == null)
		{
			mensajeError ("Error 12 (" + $COMA.getLine() + "," + ($COMA.getCharPositionInLine()+1) +
					"): demasiados indices");
		}	
		$maxStack = Math.max($maxStack, $ex2.maxStack+1);
	}	
	)*
;


miembro[String tab, Tipo clase]	:	
	campo [$clase]
	|
	metodo[$tab, $clase]
	{
		
	};

campo	[Tipo clase]
	:	
	visibilidad decl[$visibilidad.isPublic, $clase];


visibilidad
returns [boolean isPublic]
	:	 PRIVATE {$isPublic = false;} | PUBLIC {$isPublic = true;};
	
	
args
returns [int num, ArrayList<String> nombres]	:	 
	(DOUBLE id1=ID
	{
		$nombres = new ArrayList<String>();
		$num = 1;
		$nombres.add($id1.text);
		if (!tablaSimbolos.addParametro($id1.text))
		{
			mensajeError ("Error 1 (" + $id1.getLine() + "," + ($id1.getCharPositionInLine()+1) +
					"): '" + $id1.text + "' ya existe en este ambito");
		}
	}
	
	 (COMA DOUBLE id2=ID
	{
		$num++;
		$nombres.add($id2.text);
		if (!tablaSimbolos.addParametro($id2.text))
		{
			mensajeError ("Error 1 (" + $id2.getLine() + "," + ($id2.getCharPositionInLine()+1) +
					"): '" + $id2.text + "' ya existe en este ambito");
		}	
	})*)?;



tipo	
returns [Integer tipo, Integer cc, Integer cl] : 	
	ID
	{
		$tipo = tablaTipos.getTipo($ID.text);
		$cc = $ID.getLine();
		$cl = ($ID.getCharPositionInLine()+1);
		
		if ($tipo==Tipo.ERROR)
		{
			mensajeError ("Error 2 (" + $ID.getLine() + "," + ($ID.getCharPositionInLine()+1) +
				"): simbolo '"+ $ID.text +"' no ha sido declarado");	
		}
	} 
	| 
	tiposimple
	{
		$tipo = $tiposimple.tipo;
		$cc = $tiposimple.cc;
		$cl = $tiposimple.cl;
	};

params [String tab, int c, int l]
returns [String trad, int numParam, int maxStack, String parametrosMetodo]
@init{}
	:
	{
		$trad = "";
		$parametrosMetodo = "";
		$maxStack = 0;
		$numParam = 0;
	}
	(e1=expr[tab]
	{
		$numParam=1;
		$trad += $e1.trad + " //PRIMER PARAMETRO \n";
		$maxStack = Math.max($maxStack+1, $e1.maxStack);
		if ($e1.tipo == Tipo.INT)
		{
			$parametrosMetodo += "int32";
		}
		else if ($e1.tipo == Tipo.DOUBLE)
		{
			$parametrosMetodo += "float64";
		}
		else
		{
			mensajeError("Error 24 ("+ c + "," + l +
				"): tipo incompatible en el parametro");
		}
	}

	(COMA e2=expr[tab]
	{
		$numParam++;
		$trad += $e2.trad + "//SIGUIENTES PARAMETROS \n";
		$maxStack = Math.max($maxStack+1, $e2.maxStack);
		if ($e2.tipo == Tipo.INT)
		{
			$parametrosMetodo += ",int32";
		}
		else if ($e2.tipo == Tipo.DOUBLE)
		{
			$parametrosMetodo += ",float64";
		}
		else
		{
			mensajeError("Error 24 ("+ $COMA.getLine() + "," + ($COMA.getCharPositionInLine()+1) +
				"): tipo incompatible en el parametro");
		}		
	
	})*)?
	;

subref	[String tab]
returns [Integer tipo, int var, Integer maxStack, String accesoClase, String ultimoAccesoClase, Simbolo lastSimbolo, int c, int l, TablaSimbolos ts, boolean isParam]
@init {Simbolo actual; Simbolo metodoActual = null; int lastIdCol; int lastIdFil;}
:	
	id1=ID
	{
		boolean isInClass = false;
		$accesoClase = "";
		$c = $id1.getLine();
		$l = $id1.getCharPositionInLine()+1;
				$ultimoAccesoClase = "";
		
		actual = tablaSimbolos.busca($id1.text);
		metodoActual = tablaSimbolos.getMetodo($id1.text);
		
		if (metodoActual != null)
		{
			actual = metodoActual;
		}
		
		if (actual == null)
		{
			mensajeError ("Error 2 (" + $id1.getLine() + "," + ($id1.getCharPositionInLine()+1) +
					"): simbolo '"+ $id1.text +"' no ha sido declarado");	
		}

	
		if (actual.getTipoSimbolo() == Simbolo.INTERNO)
		{	
			isInClass = true;
		}

		
		$tipo = actual.getTipo();
		if (isInClass)
		{
			$accesoClase = "ldarg 0 //CARGAMOS EL THIS\n";
			$ultimoAccesoClase = "stfld ";
			if (actual.getTipo() == Tipo.INT || actual.getTipo() == Tipo.BOOLEAN)
			{
				$ultimoAccesoClase += "int32 ";
			}
			else
			{
				$ultimoAccesoClase += "float64 ";
			}
			$ultimoAccesoClase += "'"+claseActual + "'::'" + $id1.text+ "' \n";
		}
		else
		{
			$ultimoAccesoClase = $id1.text;
		}
		$var = tablaSimbolos.getPos($id1.text);
		$maxStack = 0;
		
		
		lastIdCol = $id1.getCharPositionInLine()+1;
		lastIdFil = $id1.getLine();

		$lastSimbolo = actual;
		$maxStack = 0;
		$isParam = $lastSimbolo.isParam();
		$ts = null;
		if ($lastSimbolo.isClass())
		{
			$ts = tablaTipos.get($lastSimbolo.getTipo()).getAtributos();

			$lastSimbolo = actual;
		}
	}

	(PUNTO id2=ID
	{
		if (!actual.isClass())
		{
			mensajeError ("Error 19 (" + lastIdCol + "," + lastIdFil +
				"): identificador '" + actual.getNombre() + "' usado incorrectamente");
		}
		
		
		if ($ultimoAccesoClase != "")
		{
			$accesoClase += "ldfld ";
			if (actual.getTipo() == Tipo.INT || actual.getTipo() == Tipo.BOOLEAN)
			{
				$accesoClase += "int32 ";
			}
			else
			{
				$accesoClase += "float64 ";
			}
			$accesoClase += $ultimoAccesoClase + " //Accedemos a una clase \n";
			$ultimoAccesoClase = "";
		}
		

		
		
		$ultimoAccesoClase += "'" + tablaTipos.get(actual.getTipo()).getNombre() + "'::'";
		$ultimoAccesoClase += $id2.text + "'"; 	

		metodoActual = tablaTipos.get(actual.getTipo()).getAtributos().getMetodo($id2.text);
		actual = tablaTipos.get(actual.getTipo()).getAtributos().busca($id2.text);


		if (metodoActual != null)
		{
			actual = metodoActual;
		}
		
		if (actual == null)
		{
			mensajeError ("Error 2 (" + $id2.getLine() + "," + ($id2.getCharPositionInLine()+1) +
					"): simbolo '"+ $id2.text +"' no ha sido declarado");			
		}
		if (actual.isClass())
		{
			$ts = tablaTipos.get(actual.getTipo()).getAtributos();
			$lastSimbolo = actual;
		}
		
		lastIdCol = $id2.getCharPositionInLine()+1;
		lastIdFil = $id2.getLine();
		$maxStack++;
		$tipo = actual.getTipoSimbolo();
		
		$lastSimbolo = actual;
		
	})*;



CLASS		:	'class';
VOID		:	'void';
MAIN		:	'Main';
INT		:	'int';
DOUBLE		:	'double';
BOOL		:	'bool';
PUBLIC		:	'public';
STATIC		:	'static';
IF		:	'if';
ELSE		:	'else';
FOREACH		:	'foreach';
VAR		:	'var';
IN		:	'in';
FOR		:	'for';
TO		:	'to';
STEP		:	'step';
WHILE		:	'while';
BREAK		:	'break';
CONTINUE	:	'continue';
NEW		:	'new';
RETURN		:	'return';
PRIVATE		:	'private';
WRITELINE	:	'System.Console.WriteLine';
READLINE	:	('int'|'double'|'bool')'.Parse(System.Console.ReadLine())';
LLAVEI		:	'{';
LLAVED		:	'}';
PARI		:	'(';
PARD		:	')';
CORI		:	'[';
CORD		:	']';
COMA		:	',';
PYC		:	';';
ASIG		:	'=';
OR		:	'|';
AND		:	'&';
RELOP		:	'=='|'!='|'<' | '>' | '<=' | '>=';
ADDOP		:	'+' | '-';
MULOP		:	'*' | '/';
NOT		:	'!';
PUNTO		:	'.';
ENTERO		:	('0'..'9')+;
REAL		:	('0'..'9')+'.'('0'..'9')+;
BOOLEANO	:	'True' | 'False';
ID		:	('a'..'z'|'A'..'Z')('a'..'z'|'A'..'Z'|'0'..'9'|'_')*;
COMENTARIO	:	'/*' .* '*/' {skip();};
COMENTARIO2	:	'//'~'\n'* {skip();};
NEWLINE		:	('\r'? '\n'|' '|'\t')+  {skip();};
